package com.igoal.service;

import java.util.Date;
import java.util.List;
import java.util.Map;

import org.appfuse.service.GenericManager;
import org.appfuse.service.UserExistsException;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Transactional;

import com.igoal.dto.FriendDTO;
import com.igoal.dto.FriendRequestDTO;
import com.igoal.dto.JobDTO;
import com.igoal.dto.PotentialFriendDTO;
import com.igoal.dto.ProfileCompleteDTO;
import com.igoal.dto.SchoolDTO;
import com.igoal.dto.UserDTO;
import com.igoal.dto.UserStatusDTO;
import com.igoal.entity.ComponentEnum;
import com.igoal.entity.User;
import com.igoal.exception.LoginException;

public interface UserManagerExt extends GenericManager<UserDTO, Long> {
	/**
	 * Retrieves a user by userId. An exception is thrown if user not found
	 * 
	 * @param userId
	 *            the identifier for the user
	 * @return User
	 */
	UserDTO getUser(String userId);

	/**
	 * Finds a user by their username.
	 * 
	 * @param username
	 *            the user's username used to login
	 * @return User a populated user object
	 * @throws org.springframework.security.userdetails.UsernameNotFoundException
	 *             exception thrown when user not found
	 */
	UserDTO getUserByUsername(String username) throws UsernameNotFoundException;

	/**
	 * Retrieves a list of all users.
	 * 
	 * @return List
	 */
	List<UserDTO> getUsers();

	/**
	 * Retrieves a list of users except for current user session
	 * 
	 * @return
	 */
	List<UserDTO> getUsersExceptForMe();

	/**
	 * Saves a user's information.
	 * 
	 * @param user
	 *            the user's information
	 * @throws UserExistsException
	 *             thrown when user already exists
	 * @return user the updated user object
	 */
	UserDTO saveUser(UserDTO user) throws UserExistsException;

	/**
	 * Removes a user from the database by their userId
	 * 
	 * @param userId
	 *            the user's id
	 */
	void removeUser(String userId);

	/**
	 * Validate username is unique
	 * 
	 * @param username
	 * @return 0 if valid
	 */
	long validateUsername(String username);

	/**
	 * Validate email is unique
	 * 
	 * @param email
	 * @return 0 if valid
	 */
	long validateEmail(String email);

	/**
	 * Search user by any match information with <i>text</i>
	 * 
	 * @param text
	 * @return
	 */
	List<UserDTO> searchUser(String text);

	/**
	 * Login user
	 * 
	 * @param username
	 * @param password
	 * @return
	 */
	UserDTO login(String username, String password) throws LoginException;

	/**
	 * Find contact email inside provided user account
	 * 
	 * @param vendor
	 *            {@link ContactVendorEnum}
	 * @param username
	 *            email vendor username
	 * @param password
	 *            email vendor password
	 * @return list of contact from user's email contacts
	 */
	@Transactional(readOnly = false)
	List<UserDTO> findContacts(ContactVendorEnum vendor, String username, String password);

	/**
	 * Add friend to userRequest
	 * 
	 * @param userRequest
	 *            user that request add friend
	 * @param friendId
	 *            - identifier of an exist user
	 */
	@Transactional(readOnly = false)
	void addFriend(UserDTO userRequest, Long friendId);

	/**
	 * Add friends to userRequest
	 * 
	 * @param userRequest
	 *            user that request add friend
	 * @param friendIds
	 *            - list of exist user
	 */
	@Transactional(readOnly = false)
	void addFriends(UserDTO userRequest, Long[] friendIds);

	/**
	 * Get user status of user; unless exist -> create new one
	 * 
	 * @param userId
	 *            user identifier
	 * @return user status
	 */
	@Transactional(readOnly = false)
	UserStatusDTO getUserStatus(UserDTO user);

	/**
	 * Save new status of user
	 * 
	 * @param user
	 *            owner of status
	 * @param status
	 *            content of status
	 * @return {@link SaveResult}
	 */
	@Transactional(readOnly = false)
	int saveUserStatus(UserDTO user, String status);

	@Transactional(readOnly = true)
	List<FriendDTO> getRandomFriends(UserDTO owner);

	@Transactional(readOnly = true)
	List<FriendDTO> getAllFriends(UserDTO user);
	
	@Transactional(readOnly = true)
	List<FriendDTO> getFriends(String username, int pageId);
	
	@Transactional(readOnly = true)
	int countFriends(String username);

	/**
	 * Get a user that has same friend with the owner
	 * 
	 * @param owner
	 * @return {@link Map} username, list of same friend or null
	 */
	@Transactional(readOnly = true)
	Map<String, List<FriendDTO>> getPeopleYouMayKnow(UserDTO owner);

	@Transactional(readOnly = false)
	int saveUserAvatar(UserDTO user, String avatarURL);

	@Transactional(readOnly = false)
	int likeThisComponent(User user, ComponentEnum component, Long componentId);

	@Transactional(readOnly = false)
	int likeThisDoneEntry(String username, Long entryId);

	@Transactional(readOnly = false)
	int likeThisEntry(String username, Long entryId);

	@Transactional(readOnly = false)
	int unlikeThisDoneEntry(String username, Long entryId);

	@Transactional(readOnly = false)
	int unlikeThisEntry(String username, Long entryId);

	@Transactional(readOnly = false)
	int unlikeThisComponent(User user, ComponentEnum component, Long componentId);

	/**
	 * Save user's basic information
	 * 
	 * @param user
	 * @return
	 */
	@Transactional(readOnly = false)
	int saveUserBasicInfo(UserDTO user);

	/**
	 * Save user's contact information
	 * 
	 * @param user
	 * @return
	 */
	@Transactional(readOnly = false)
	int saveUserContactInfo(UserDTO user);

	/**
	 * Save user's schools, jobs
	 * 
	 * @param user
	 * @return
	 */
	@Transactional(readOnly = false)
	int saveUserJobInfo(UserDTO user);

	/**
	 * Save user's hobbies
	 * 
	 * @param user
	 * @return
	 */
	@Transactional(readOnly = false)
	int saveUserHobbyInfo(UserDTO user);

	/**
	 * Change user's password
	 * 
	 * @param user
	 * @return
	 */
	@Transactional(readOnly = false)
	int saveUserPasswordInfo(UserDTO user, String oldPassword, String newPassword);

	@Transactional(readOnly = false)
	int savePassword(UserDTO user, String password);

	@Transactional(readOnly = false)
	int saveAboutMe(String username, String aboutMe);

	/**
	 * Check if this is the password of the current user
	 * 
	 * @param password
	 * @return
	 */
	boolean checkPassword(String password);

	@Transactional(readOnly = true)
	boolean isFriend(UserDTO user1, UserDTO user2);

	@Transactional(readOnly = true)
	boolean isWaitingAcceptFriend(UserDTO from, UserDTO to);

	@Transactional(readOnly = false)
	int acceptFriend(String from, String to);

	@Transactional(readOnly = false)
	int refuseFriend(String from, String to);

	@Transactional(readOnly = true)
	List<FriendRequestDTO> getFriendRequests(String username);

	@Transactional(readOnly = true)
	List<PotentialFriendDTO> getPeopleYouMayKnow(UserDTO owner, boolean onlineOnly);

	@Transactional(readOnly = true)
	List<FriendDTO> getMutualFriends(UserDTO owner, UserDTO friendOfFriend);

	ProfileCompleteDTO getProfileComplete(UserDTO user);

	@Transactional(readOnly = false)
	int addCategories(String username, Long[] categoryIds);

	@Transactional(readOnly = false)
	int removeCategories(String username, Long[] categoryIds);

	/**
	 * Get user's jobs
	 * 
	 * @param user
	 * @return
	 */
	@Transactional(readOnly = true)
	List<JobDTO> getJobInfo(UserDTO user);

	/**
	 * Save user's jobs
	 * 
	 * @param user
	 * @param jobs
	 * @return
	 */
	@Transactional(readOnly = false)
	void saveJobInfo(UserDTO user, List<JobDTO> jobs);

	/**
	 * Get user's schools
	 * 
	 * @param user
	 * @return
	 */
	@Transactional(readOnly = true)
	List<SchoolDTO> getSchoolInfo(UserDTO user);

	/**
	 * Save user's school
	 * 
	 * @param user
	 * @param jobs
	 * @return
	 */
	@Transactional(readOnly = false)
	void saveSchoolInfo(UserDTO user, List<SchoolDTO> jobs);

	@Transactional(readOnly = true)
	boolean isDisable(String username);
	
	boolean checkActivationCode(String username, String code);
	
	int saveActionvationCode(UserDTO user, String code);
	
	int setCredentialsExpired(UserDTO userDTO, boolean expired);

	@Transactional(readOnly = true)
	List<PotentialFriendDTO> getAdmin(UserDTO userDTO);
	
	@Transactional(readOnly = false)
	int thumbUpUserStatus(String username, Long statusId);
	
	@Transactional(readOnly = false)
	int thumbDownUserStatus(String username, Long statusId);
	
	/**
	 * 
	 * @param username
	 * @param statusId
	 * @return null if user hasn't thumb this status or some error happened. <br/>
	 * TRUE if thumbed up; otherwise return FALSE
	 */
	@Transactional(readOnly = true)
	Boolean getUserThumbedStatus(String username, Long statusId);
	
	@Transactional(readOnly = true)
	int countUserStatusThumbedUp(Long statusId);
	
	@Transactional(readOnly = true)
	int countUserStatusThumbedDown(Long statusId);
	
	@Transactional(readOnly = false)
	int markReadNotification(String username, Long[] notificationIds);
	
	@Transactional(readOnly = false)
	int cheerThisComponent(User user, ComponentEnum component, Long componentId);

	@Transactional(readOnly = false)
	int cheerThisDoneEntry(String username, Long entryId);

	@Transactional(readOnly = false)
	int cheerThisEntry(String username, Long entryId);
	
	@Transactional(readOnly = true)
	List<FriendRequestDTO> getFriendRequestsByPage(String username, Date lastOrderPropertyValue, String orderProperty, boolean before);
	
	@Transactional(readOnly = true)
	int countNewFriendRequests(String username);
	
	@Transactional(readOnly = true)
	int refuseSilenceFriend(String from, String to);
	
	@Transactional(readOnly = false)
	long addWallPost(String postOwner, String wallOwner, String thought, String privacy);

	@Transactional(readOnly = false)
	int deleteWallPost(Long id, String whoDelete);
	
	@Transactional(readOnly = true)
	List<PotentialFriendDTO> searchFriend(String username, String keyword, int page);
	
	@Transactional(readOnly = true)
	boolean checkSearchFriendResult(String username, String keyword, int page);
	
	@Transactional(readOnly = true)
	int countSearchResult(String username, String keyword);
	
	@Transactional(readOnly = true)
	List<UserDTO> getUserHavingRole(String roleName);
	
	@Transactional(readOnly = false)
	UserDTO saveSubcriptionType(String username, Integer subscriptionType, boolean receiveSubscription);
}
